#include <iostream>
#include <fstream>
#include "addressbook.pb.h"
#include <google/protobuf/any.h>

using namespace std;

void SaveInfo()
{
	AddressBookInfo::AddressBook adbook;

	// 使用set_设置message中filed的值，函数名都是小写（即使在proto中是大写）
	adbook.set_owner("eric");

	// Person1
	// 使用add_添加message，返回的是指针类型
	AddressBookInfo::Person *person1 = adbook.add_person_infos();
	person1->set_name("mark");
	person1->set_id(123456);
	person1->set_email("mark@163.com");

	AddressBookInfo::Person::PhoneNumber *person1_phone = person1->add_phones();
	person1_phone->set_number("12345678");
	// 枚举类型中的元素名称是唯一的，所以可以直接用作用域限定符访问元素，不需要通过枚举名访问
	person1_phone->set_type(AddressBookInfo::Person::HOME);

	person1_phone = person1->add_phones();
	person1_phone->set_number("1234");
	person1_phone->set_type(AddressBookInfo::Person::WORK);

	// Person2
	AddressBookInfo::Person *person2 = adbook.add_person_infos();
	person2->set_name("mike");
	person2->set_id(654321);
	person2->set_email("mike@163.com");

	AddressBookInfo::Person::PhoneNumber *person2_phone = person2->add_phones();
	person2_phone->set_number("87654321");
	person2_phone->set_type(AddressBookInfo::Person::HOME);

	person2_phone = person2->add_phones();
	person2_phone->set_number("5678");
	person2_phone->set_type(AddressBookInfo::Person::WORK);

	// map和any类型的初始化，mutable返回的是非const指针类型
	google::protobuf::Map<string, google::protobuf::Any> *owner_address = adbook.mutable_owner_address();
	// 定义一个Any类型，用于接收message
	google::protobuf::Any *any = new google::protobuf::Any;
	AddressBookInfo::Address adbook_address;
	adbook_address.set_address("HB");
	// 使用PackFrom将message类型存储为Any类型
	any->PackFrom(adbook_address);
	// map类型的初始化方式和STL中的map类似
	(*owner_address)[adbook.owner()] = *any;

	// 设置oneof中某一个成员的值，之后如果再使用set_则会core dump
	adbook.set_type_ali("AliPay");

	fstream output("address_book_file", ios::out | ios::trunc | ios::binary);
	// 使用SerializeToOstream将序列化后的数据写入文件中
	if (!adbook.SerializeToOstream(&output))
	{
		cerr << "Failed to write address book." << endl;
	}
}

void ShowMsg(const AddressBookInfo::AddressBook &adbook)
{
	cout << adbook.owner() << endl;
	// 对于重复message，_size表示有多少个重复message，使用索引取出每一个message
	for (int i = 0; i < adbook.person_infos_size(); ++i)
	{
		const AddressBookInfo::Person &person = adbook.person_infos(i);
		// 取出各个字段的值
		cout << person.name() << endl;
		cout << person.id() << endl;
		cout << person.email() << endl;
		for (int j = 0; j < person.phones_size(); ++j)
		{
			// 使用索引获取枚举类型的所有值
			const AddressBookInfo::Person::PhoneNumber &person_phone = person.phones(j);
			switch(person_phone.type()) 
			{
				case AddressBookInfo::Person::MOBILE :
					cout << "MOBILE phone #: ";
					break;
				case AddressBookInfo::Person::HOME :
					cout << "HOME phone #: ";
					break;
				case AddressBookInfo::Person::WORK :
					cout << "WORK phone #: ";
					break;
			}
			cout << person_phone.number() << endl;
		}
	}
	cout << adbook.type_ali() << endl;
}

void ShowMapMsg(const AddressBookInfo::AddressBook &adbook) 
{
	// 取出map类型的字段成员
	const google::protobuf::Map<string, google::protobuf::Any> &adbook_map = adbook.owner_address();
	// 使用迭代器访问map（和STL中的map类似）
	auto iter = adbook_map.begin();
	cout << iter->first << endl;
	google::protobuf::Any any = iter->second;
	AddressBookInfo::Address adbook_address;
	// 使用UnpackTo从Any类型解析出message，注意参数中有一个&
	if (any.UnpackTo(&adbook_address))
	{
		cout << adbook_address.address() << endl;
	}
	else
	{
		cout << "UnpackTo data error" << endl;
	}
}

void LoadInfo()
{
	AddressBookInfo::AddressBook adbook;
	fstream input("address_book_file", ios::in | ios::binary);
	// 使用ParseFromIstream从文件中反序列化
	if (!input)
	{
		cout << ": File not found.  Creating a new file." << endl;
	}
	else if (!adbook.ParseFromIstream(&input))
	{
		cout << "Failed to parse address book." << endl;
	}
	ShowMsg(adbook);
	cout << endl;
	ShowMapMsg(adbook);
}

int main(int argc, char const *argv[])
{
	SaveInfo();
	LoadInfo();
	// 删除所有已分配的内存（注意any是堆上的内存，清除内存的操作要小心）
	google::protobuf::ShutdownProtobufLibrary();
	return 0;
}